package com.jkgh.tnh.rs;

import com.jkgh.asin.*;
import com.jkgh.asin.buffers.*;
import com.jkgh.remedium.*;
import com.jkgh.remedium.objects.*;
import com.jkgh.remedium.objects.messages.*;
import com.jkgh.remedium.objects.messages.out.*;
import com.jkgh.remedium.states.*;
import com.jkgh.remedium.tools.*;

/**
 * Some description.
 */
public abstract class TNHSController extends RemediumMethodObject {

	/**
	 * Some description.
	 * 
	 * @param token Some description.
	 * @param intents Some description.
	 * 
	 * @param returner Should return: Some description.. 
	 */
	public abstract void logIn(String token, int[] intents, Returner<String> returner);

	/**
	 * Some description.
	 * 
	 */
	public abstract void logOut();

	/**
	 * Some description.
	 * 
	 * @param controller Some description.
	 * @param accesses Some description.
	 */
	public abstract void doThings(TNHSController controller, TNHAccess[] accesses);

	// --------------------------------------------------------------------------------

	public TNHSController(RemediumServer server) {
		super(server);
	}

	protected TNHSController(int hostID, int objectID, RemediumServer server) {
		super(hostID, objectID, server);
	}

	@Override
	public void call(final StateMachine machine, final RemediumInMessage message, final int callID, int methodID, final Callback callback) {
		switch (methodID) {

			case 0:
				RemediumTools.consumeString(machine, new Future<String>() {

					@Override
					public void goWith(final String token0) {
						RemediumTools.consumeIntArray(machine, new Future<int[]>() {

							@Override
							public void goWith(final int[] intents0) {
								logIn(token0, intents0, new Returner<String>() {
								
									@Override
									public void returned(String r) {
										RemediumS2CStringResponder responder = new RemediumS2CStringResponder(callID, r);
										responder.produce(machine);
									}
								});
								callback.go();
							}
						});
					}
				});
				break;

			case 1:
				logOut();
				callback.go();
				break;

			case 2:
				TNHSController.consume(machine, message, new Future<TNHSController>() {

					@Override
					public void goWith(final TNHSController controller2) {
						TNHAccess.consumeArray(machine, message, new Future<TNHAccess[]>() {

							@Override
							public void goWith(final TNHAccess[] accesses2) {
								doThings(controller2, accesses2);
								callback.go();
							}
						});
					}
				});
				break;
		}
	}

	public static void consume(final StateMachine machine, final RemediumInMessage message, final Future<TNHSController> future) {
		machine.setCurrentState(new GatheringState(machine, 1) {

			@Override
			protected void onNeededBytesAvailable() {
				switch (machine.produceByte()) {

					case RemediumConstants.NULL:
						future.goWith(null);
						break;

					case RemediumConstants.HISTORY:
						future.goWith((TNHSController) message.fromHistory(machine.produceInt()));
						break;

					case RemediumConstants.VALUE:
						consumeContent(machine, message, future);
						break;
				}
			}
		});
	}

	public static void consumeArray(final StateMachine machine, final RemediumInMessage message, final Future<TNHSController[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {

			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new TNHSController[0]);
				} else {
					consume(machine, message, new Future<TNHSController>() {

						private int i = 0;
						private TNHSController[] result = new TNHSController[length];

						@Override
						public void goWith(TNHSController o) {
							result[i++] = o;
							if (i == length) {
								future.goWith(result);
							} else {
								consume(machine, message, this);
							}
						}
					});
				}
			}
		});
	}

	private static void consumeContent(final StateMachine machine, final RemediumInMessage message, final Future<TNHSController> future) {
		machine.setCurrentState(new GatheringState(machine, 8) {

			@Override
			protected void onNeededBytesAvailable() {

				int hostID = machine.produceInt();
				int objectID = machine.produceInt();

				RemediumHost host = message.getServer().getHost(hostID);
				TNHSController methodObject = (TNHSController) host.getMethodObject(objectID);
				if (methodObject == null) {
					methodObject = new TNHSControllerRedirector(hostID, objectID, message.getServer());
					host.registerMethodObject(objectID, methodObject);
				}

				message.toHistory(methodObject);

				future.goWith(methodObject);
			}
		});
	}

	private static class TNHSControllerRedirector extends TNHSController {

		private final RemediumHost host;

		protected TNHSControllerRedirector(int hostID, int objectID, RemediumServer server) {
			super(hostID, objectID, server);

			this.host = server.getHost(hostID);
		}

		@Override
		public void logIn(final String token, final int[] intents, Returner<String> returner) {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 0, host.registerCall(returner)) {

				@Override
				protected void produceArguments(DataConsumer consumer) {
					RemediumTools.produceString(consumer, token);
					RemediumTools.produceIntArray(consumer, intents);
				}
			};
			caller.produce(host.getMachine());
		}

		@Override
		public void logOut() {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 1, 0) {

				@Override
				protected void produceArguments(DataConsumer consumer) {
				}
			};
			caller.produce(host.getMachine());
		}

		@Override
		public void doThings(final TNHSController controller, final TNHAccess[] accesses) {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 2, 0) {

				@Override
				protected void produceArguments(DataConsumer consumer) {
					this.produceObject(controller, consumer);
					RemediumTools.produceObjectArray(consumer, this, accesses);
				}
			};
			caller.produce(host.getMachine());
		}
	}
}
